#include <fs.h>
#include <string.h>
#include <list.h>
#include <print.h>

#define FS_MAGIC        "KFS-0.1"   // 文件系统名称&版本
#define ROOT_MAGIC      "root"      // 根目录名     

#define SEC_END_FLAG    ((U32)-1)   // 结束标志
#define MAP_UNIT_NUM    (SEC_SIZE/sizeof(U32))              // 一个扇区内包含的管理单元数

static LIST fdList;

// 文件类型
typedef enum E_F_TYPE
{
    E_FILE,                         // 文件
    E_DIR                           // 文件夹
} E_F_TYPE;

// 记录文件系统概要信息，存储于 0 号扇区
typedef struct FS_HEADER
{
    U08         forJmp[4];          // 预留 4 字节给跳转指令使用
    U08         magic[32];          // 文件系统名
    U32         sctNum;             // 文件系统可以使用的扇区总数
    U32         mapNum;             // 扇区分配表所占的扇区数
    U32         freeBegin;          // 我们把空闲扇区组织成一个链表，freeBegin 为第一个空闲的扇区号
    U32         freeNum;            // 空闲扇区数   
} FS_HEADER;

// 记录根目录相关信息，存储于 1 号扇区
typedef struct FS_ROOT
{
    U08         magic[32];          // 根目录名
    U32         rootBegin;          // 将根目录所涉及的所有扇区组织成一个链表，rootBegin 为第一个扇区号
    U32         rootNum;            // 根目录所占的扇区数 
    U32         lastBytes;          // 最后一个扇区中的有效数据字节数
} FS_ROOT;

// 记录一个文件的基本信息
typedef struct FILE_ENTRY
{
    U08         name[32];           // 文件名
    U32         fileBegin;          // 文件的第一个扇区号
    U32         fileNum;            // 文件占用的扇区数
    U32         lastBytes;          // 最后一个扇区中的有效数据字节数
    E_F_TYPE    type;               // 文件类型（文件/文件夹）
    U32         inSecIdx;           // FILE_ENTRY 这个数据结构本身也要存储在硬盘上，inSecIdx 为 FILE_ENTRY 这个数据结构本身存储在扇区号
    U32         inSecOff;           // FILE_ENTRY 这个数据结构本身也要存储在硬盘上，inSecOff 为 FILE_ENTRY 这个数据结构本身存储在扇区中的偏移量
    U32         resered[2];         // 保留
} FILE_ENTRY;

typedef struct FILE_DESC
{
    LIST_NODE   node;
    FILE_ENTRY  fe;
    U32         secIdx;             // 文件数据链表节点偏移
    U32         offset;             // 用于记录文件读写位置
    U08         changed;            // 数据是否被修改
    U08         catch[SEC_SIZE];    // 数据缓冲区
} FILE_DESC;


/******************************************************************************
* 函数名称: static U32 AllocSector(void)
* 功能说明: 申请一个扇区
* 输入参数: 无
* 输出参数: 无
* 函数返回: 申请到的扇区号
* 其它说明: 申请到的扇区号为实际的物理扇区号
******************************************************************************/
static U32 AllocSector(void)
{
    U32 ret = SEC_END_FLAG;
    FS_HEADER* header = (FS_HEADER *)FS_MALLOC(SEC_SIZE);   // 申请一个扇区大小的内存用于文件概要信息（扇区 0）数据处理使用
    U32 offset = 0;
    U32 idxOff = 0;
    U32 secOff = 0;
    U32* pUnit = (U32 *)FS_MALLOC(SEC_SIZE);                // 申请一个扇区大小的内存用于读取扇区分配表中的一个扇区数据
    U32 next = 0;

    if(NULL == header || SEC_END_FLAG == header->freeBegin)
        goto error;

    if(E_ERR == FS_READ(0, (U08 *)header))                  // 从扇区 0 中读出 header 头信息
        goto error;

    ret = header->freeBegin;                                // 返回 freeBegin 链表后的第一个节点
    offset = header->freeBegin - header->mapNum - 2;        // 计算 freeBegin 这个绝对地址（物理扇区号）在扇区分配表中对应的管理单元的相对偏移位置
    secOff = offset / MAP_UNIT_NUM;                         // 计算目标扇区对应的管理单元处在扇区分配表中的第几个扇区，secOff+2 才是其绝对地址（物理扇区号）
    idxOff = offset % MAP_UNIT_NUM;                         // 计算目标扇区对应的管理单元处在扇区分配表中某一扇区中的偏移量
    if(E_ERR == FS_READ(secOff+2, (U08 *)pUnit))            // 读取目标扇区对应的管理单元所处的整个扇区数据
        goto error;
    next = *(pUnit + idxOff);                               // 得到 next 的值
    *(pUnit + idxOff) = SEC_END_FLAG;                       // 由于 (pUnit + idxOff) 这个管理单元对应的扇区被分配出去，所以给其对应的管理单元赋一个无效值
    if(E_ERR == FS_WRITE(secOff+2, (U08 *)pUnit))           // 将 pUnit 数据写回扇区分配表中对应的扇区中
        goto error;
    header->freeBegin = next + 2 + header->mapNum;          // header->freeBegin 指向下一个节点，注意 freeBegin 存的是物理扇区号
    header->freeNum--;                                      // 空闲扇区数 - 1
    if(E_ERR == FS_WRITE(0, (U08 *)header))                 // 将 header 头信息写回扇区 0 中
        goto error;

    FS_FREE(header);
    FS_FREE(pUnit);
    return ret;

error:
    FS_FREE(header);
    FS_FREE(pUnit);
    return SEC_END_FLAG;
}

/******************************************************************************
* 函数名称: static E_RET FreeSector(U32 sn)
* 功能说明: 释放一个扇区
* 输入参数: U32 sn      --要释放的扇区号
* 输出参数: 无
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 释放的扇区号为实际的物理扇区号
******************************************************************************/
static E_RET FreeSector(U32 sn)
{
    FS_HEADER* header = (FS_HEADER *)FS_MALLOC(SEC_SIZE);   // 申请一个扇区大小的内存用于文件概要信息（扇区 0）数据处理使用
    U32 offset = 0;
    U32 idxOff = 0;
    U32 secOff = 0;
    U32* pUnit = (U32 *)FS_MALLOC(SEC_SIZE);                // 申请一个扇区大小的内存用于读取扇区分配表中的一个扇区数据

    if(NULL == header || SEC_END_FLAG == header->freeBegin)
        goto error;

    if(E_ERR == FS_READ(0, (U08 *)header))                  // 从扇区 0 中读出 header 头信息
        goto error;

    offset = sn - header->mapNum - 2;                       // 计算 sn 这个绝对地址（物理扇区号）在扇区分配表中对应的管理单元的相对偏移位置
    if(offset >= header->mapNum)                            // sn 范围超限
        return E_ERR;
    secOff = offset / MAP_UNIT_NUM;                         // 计算目标扇区对应的管理单元处在扇区分配表中的第几个扇区，secOff+2 才是其绝对地址（物理扇区号）
    idxOff = offset % MAP_UNIT_NUM;                         // 计算目标扇区对应的管理单元处在扇区分配表中某一扇区中的偏移量
    if(E_ERR == FS_READ(secOff+2, (U08 *)pUnit))            // 读取目标扇区对应的管理单元所处的整个扇区数据
        goto error;
    *(pUnit + idxOff) = header->freeBegin - 2  - header->mapNum; // 采用头插的方式，新插入的节点指向原头结点；由于扇区分配表中存的是相对地址，故 header->freeBegin 需要转成相对地址
    if(E_ERR == FS_WRITE(secOff+2, (U08 *)pUnit))           // 将 pUnit 数据写回扇区分配表中对应的扇区中
        goto error;
    header->freeBegin = sn;                                 // header->freeBegin 指向新插入的节点
    header->freeNum++;                                      // 空闲扇区数 + 1
    if(E_ERR == FS_WRITE(0, (U08 *)header))                 // 将 header 头信息写回扇区 0 中
        goto error;

    FS_FREE(header);
    FS_FREE(pUnit);
    return E_OK;

error:
    FS_FREE(header);
    FS_FREE(pUnit);
    return E_ERR;
}

/******************************************************************************
* 函数名称: static U32 NextSector(U32 sn)
* 功能说明: 查找扇区号 sn 的下一个扇区号
* 输入参数: U32 sn          --扇区号
* 输出参数: 无
* 函数返回: 下一个扇区号（物理扇区号）
* 其它说明: 无
******************************************************************************/
static U32 NextSector(U32 sn)
{
    U32 next = SEC_END_FLAG;
    U32 offset = 0;
    U32 idxOff = 0;
    U32 secOff = 0;
    FS_HEADER* header = (FS_HEADER *)FS_MALLOC(SEC_SIZE);   // 申请一个扇区大小的内存用于文件概要信息（扇区 0）数据处理使用
    U32* pUnit = (U32 *)FS_MALLOC(SEC_SIZE);                // 申请一个扇区大小的内存用于读取扇区分配表中的一个扇区数据
    if(NULL == header || NULL == pUnit || SEC_END_FLAG == sn)
        goto error;
    
    if(E_ERR == FS_READ(0, (U08 *)header))                  // 从扇区 0 中读出 header 头信息
        goto error;

    // 计算物理扇区号 sn 在扇区分配表中的位置
    offset = sn - header->mapNum - 2;                       // 计算 sn 这个绝对地址（物理扇区号）在扇区分配表中对应的管理单元的相对偏移位置
    if(offset >= header->mapNum)                            // sn 范围超限
        return E_ERR;
    secOff = offset / MAP_UNIT_NUM;                         // 计算目标扇区对应的管理单元处在扇区分配表中的第几个扇区，secOff+2 才是其绝对地址（物理扇区号）
    idxOff = offset % MAP_UNIT_NUM;                         // 计算目标扇区对应的管理单元处在扇区分配表中某一扇区中的偏移量
    
    if(E_ERR == FS_READ(secOff+2, (U08 *)pUnit))            // 读取目标扇区对应的管理单元所处的整个扇区数据
        goto error;
    next = *(pUnit + idxOff);                               // 得到下一个扇区的相对地址
    if(SEC_END_FLAG == next)                                // 如果已经是最后一个扇区了，则直接返回 SEC_END_FLAG
        goto error;
    next = next + header->mapNum + 2;                       // 由相对地址计算出绝对地址（物理扇区号）

error:
    FS_FREE(header);
    FS_FREE(pUnit);
    return next;
}

/******************************************************************************
* 函数名称: static FILE_ENTRY* FindInRoot(const U08* name)
* 功能说明: 查找根目录下是否存在 name 文件
* 输入参数: const U08* name      --文件名
* 输出参数: 无
* 函数返回: FILE_ENTRY 数据结构首地址
* 其它说明: 返回的 FILE_ENTRY* 使用完成后需要自己手动释放
******************************************************************************/
static FILE_ENTRY* FindInRoot(const U08* name)
{
    FILE_ENTRY* ret = NULL;
    U32 i = 0;
    U32 j = 0;  
    U32 next = SEC_END_FLAG;
    FS_ROOT* root = (FS_ROOT *)FS_MALLOC(SEC_SIZE);
    U08* buf = (U08 *)FS_MALLOC(SEC_SIZE);  
    FILE_ENTRY* fe = NULL;         
    
    if(NULL == root || NULL == buf)
        goto error;
    
    if(E_ERR == FS_READ(1, (U08 *)root))                    // 将扇区 1 中数据读到 root 内存中
        goto error;

    if(0 == root->rootNum || SEC_END_FLAG == root->rootBegin)
        goto error;

    next = root->rootBegin;                                 // next 指向起始扇区号
    for(i = 0; i < root->rootNum-1; i++)                    // 遍历所有扇区（除最后一个扇区）
    {
        if(E_ERR == FS_READ(next, buf))                     // 将扇区中的数据读到 buf 缓存中
            goto error;
        for(j = 0; j < SEC_SIZE/sizeof(FILE_ENTRY); j++)    // 以 FILE_ENTRY 为单位遍历
        {
            fe  = (FILE_ENTRY *)buf + j;
            if(TRUE == StrCmp(fe->name, name, -1))          // 如果有 name == FILE_ENTRY.name 则 FILE_ENTRY 返回首地址
            {
                ret = (FILE_ENTRY *)FS_MALLOC(sizeof(FILE_ENTRY));
                if(NULL == ret)
                    goto error;
                MemCpy((U08 *)ret, (U08 *)fe, sizeof(FILE_ENTRY));
                break;
            }
        }
        if(NULL != ret)                                     // 如果已经找到了，则直接退出循环
            break;
        next = NextSector(next);                            // 指向下一个扇区
        if(SEC_END_FLAG == next)
            goto error;
    }   
    if(NULL == ret)                                         // 最后一个扇区因为不满所以要单独处理，ret == NULL，说明前面没有找到
    {
        if(E_ERR == FS_READ(next, buf))                     // 将扇区中的数据读到 buf 缓存中
            goto error;
        for(j = 0; j < root->lastBytes/sizeof(FILE_ENTRY); j++)    // 以 FILE_ENTRY 为单位遍历
        {
            fe  = (FILE_ENTRY *)buf + j;
            if(TRUE == StrCmp(fe->name, name, -1))          // 如果有 name == FILE_ENTRY.name 则返回 FILE_ENTRY 首地址
            {
                ret = (FILE_ENTRY *)FS_MALLOC(sizeof(FILE_ENTRY));
                if(NULL == ret)
                    goto error;
                MemCpy((U08 *)ret, (U08 *)fe, sizeof(FILE_ENTRY));
                break;
            }
        }
    }

error:
    FS_FREE(root);
    FS_FREE(buf);
    return ret;
}

/******************************************************************************
* 函数名称: static U32 FindLast(U32 secBegin)
* 功能说明: 查找最后一个扇区号
* 输入参数: U32 secBegin		--链表起始扇区
* 输出参数: 无
* 函数返回: 最后扇区号
* 其它说明: 无
******************************************************************************/
static U32 FindLast(U32 secBegin)
{
    U32 last = SEC_END_FLAG;
    U32 next = secBegin;
    while(next != SEC_END_FLAG)
    {
        last = next;
        next = NextSector(next);
    }
    return last;
}

/******************************************************************************
* 函数名称: static U32 FindPrev(U32 secBegin)
* 功能说明: 找到链表 secBegin 中节点扇区号为 sn 的前一个节点扇区号
* 输入参数: U32 secBegin		--链表起始扇区
    　　　　U32 sn              --扇区号
* 输出参数: 无
* 函数返回: 前一个节点扇区号
* 其它说明: 无
******************************************************************************/
static U32 FindPrev(U32 secBegin, U32 sn)
{
    U32 prev = SEC_END_FLAG;
    U32 next = secBegin;
    while((next != SEC_END_FLAG) && (next != sn))
    {
        prev = next;
        next = NextSector(next);
    }
    return prev;
}

/******************************************************************************
* 函数名称: static E_RET AddToLast(U32 secBegin, U32 sn)
* 功能说明: 把扇区号 sn 添加到链表 secBegin 的末尾
* 输入参数: U32 secBegin		--链表起始扇区
    　　　　U32 sn              --要加入的扇区号
* 输出参数: 无
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 无
******************************************************************************/
static E_RET AddToLast(U32 secBegin, U32 sn)
{
    E_RET ret = E_ERR;
    U32 last = SEC_END_FLAG;
    U32 offset = 0;
    U32 idxOff = 0;
    U32 secOff = 0;
    U32 idxOff2 = 0;
    U32 secOff2 = 0;
    FS_HEADER* header = (FS_HEADER *)FS_MALLOC(SEC_SIZE);   // 申请一个扇区大小的内存用于文件概要信息（扇区 0）数据处理使用
    U32* pUnit = (U32 *)FS_MALLOC(SEC_SIZE);                // 申请一个扇区大小的内存用于读取扇区分配表中的一个扇区数据
    
    if(NULL == header || NULL == pUnit || SEC_END_FLAG == secBegin)
        goto error;
    
    // 首先利用 NextSector 函数找到 secBegin 链表的最后一个节点，然后将 sn 节点插入到最后一个节点的后面
    last = FindLast(secBegin);
    if(SEC_END_FLAG == last)
        goto error;
    
    if(E_ERR == FS_READ(0, (U08 *)header))                  // 从扇区 0 中读出 header 头信息
        goto error;

    // 计算物理扇区号 secBegin 在扇区分配表中的位置
    offset = last - header->mapNum - 2;                     // 计算 last 这个绝对地址（物理扇区号）在扇区分配表中对应的管理单元的相对偏移位置
    if(offset >= header->mapNum)                            // last 范围超限
        goto error;
    secOff = offset / MAP_UNIT_NUM;                         // 计算目标扇区对应的管理单元处在扇区分配表中的第几个扇区，secOff+2 才是其绝对地址（物理扇区号）
    idxOff = offset % MAP_UNIT_NUM;                         // 计算目标扇区对应的管理单元处在扇区分配表中某一扇区中的偏移量

    // 计算物理扇区号 secBegin 在扇区分配表中的位置
    offset = sn - header->mapNum - 2;                       // 计算 sn 这个绝对地址（物理扇区号）在扇区分配表中对应的管理单元的相对偏移位置
    if(offset >= header->mapNum)                            // sn 范围超限
        goto error;
    secOff2 = offset / MAP_UNIT_NUM;                        // 计算目标扇区对应的管理单元处在扇区分配表中的第几个扇区，secOff+2 才是其绝对地址（物理扇区号）
    idxOff2 = offset % MAP_UNIT_NUM;                        // 计算目标扇区对应的管理单元处在扇区分配表中某一扇区中的偏移量

    if(E_ERR == FS_READ(secOff+2, (U08 *)pUnit))            // 读取目标扇区对应的管理单元所处的整个扇区数据
        goto error;
    *(pUnit + idxOff) = secOff2*MAP_UNIT_NUM + idxOff2;     // 原最后一个节点要指向新加的节点（节点值得是扇区分配表中的扇区管理单元）
    
    if(secOff == secOff2)                                   // 如果两个管理单元处在同一个扇区中
    {
        *(pUnit + idxOff2) = SEC_END_FLAG;                  // 最后一个扇区管理单元中的值应为 SEC_END_FLAG，表明这是最后一个扇区
        if(E_ERR == FS_WRITE(secOff+2, (U08 *)pUnit))       // 将 pUnit 数据写回扇区分配表中对应的扇区中
            goto error;
    }
    else                                                    // 如果两个管理单元不处在同一个扇区中
    {
        if(E_ERR == FS_WRITE(secOff+2, (U08 *)pUnit))       // 将 pUnit 数据写回扇区分配表中对应的扇区中
            goto error;
        if(E_ERR == FS_READ(secOff2+2, (U08 *)pUnit))       // 读取目标扇区对应的管理单元所处的整个扇区数据
            goto error;
        *(pUnit + idxOff2) = SEC_END_FLAG;                  // 最后一个扇区管理单元中的值应为 SEC_END_FLAG，表明这是最后一个扇区
        if(E_ERR == FS_WRITE(secOff2+2, (U08 *)pUnit))      // 将 pUnit 数据写回扇区分配表中对应的扇区中
            goto error;
    }
    ret = E_OK;

error:
    FS_FREE(header);
    FS_FREE(pUnit);
    return ret;
}

/******************************************************************************
* 函数名称: E_RET CreatFileInRoot(const U08* name)
* 功能说明: 在根目录 root 下创建一个文件
* 输入参数: const U08* name      --要创建的文件名
* 输出参数: 无
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 无
******************************************************************************/
E_RET CreatFileInRoot(const U08* name)
{
    E_RET ret = E_ERR;
    FS_ROOT* root = NULL;
    FILE_ENTRY* fe = NULL;
    U32 sn = SEC_END_FLAG;
    U32 last = SEC_END_FLAG;
    U08* buf = NULL;

    // 1. 查找文件是否已经存在，如果不存在，方可创建该文件
    fe = FindInRoot(name);
    if(NULL != fe)                                          // 文件已存在
        goto error;  
    root = (FS_ROOT *)FS_MALLOC(SEC_SIZE);                  // 申请一个扇区大小的内存用于根目录信息（扇区 1）数据处理使用
    if(NULL == root)
        goto error;
    if(E_ERR == FS_READ(1, (U08 *)root))                    // 将扇区 1 中数据读到 root 缓存中
        goto error;

    // 2. 检查 root 文件数据区的最后一个扇区空间是否足够，如果不够，需再申请一个空闲扇区
    if(0 == root->rootNum)                                  // 如果根目录尚未分配一个扇区，则分配一个扇区作为第一个扇区
    {
        sn =  AllocSector();                                // 申请一个空闲扇区
        if(SEC_END_FLAG == sn)
            goto error;
        root->rootBegin = sn;
        root->rootNum++;
        root->lastBytes = 0;
        if(E_ERR == FS_WRITE(1, (U08 *)root))               // 将 root 数据写回扇区 1
            goto error;
    }
    else
    {
        if(root->lastBytes + sizeof(FILE_ENTRY) > SEC_SIZE) // 如果最后一个扇区的空余数据区不够一个 FILE_ENTRY，则申请一个空闲扇区并插到末尾
        {
            sn =  AllocSector();
            if(SEC_END_FLAG == sn)
                goto error;
            if(E_ERR == AddToLast(root->rootBegin, sn))     // 将新申请的扇区号 sn 添加到根目录扇区链表末尾
                goto error;
            root->rootNum++;
            root->lastBytes = 0;
            if(E_ERR == FS_WRITE(1, (U08 *)root))           // 将 root 数据写回扇区 1
                goto error;
        }
    }
    
    // 3. 找到 root 文件数据区的最后一个扇区的数据末尾处
    last = FindLast(root->rootBegin);
    if(SEC_END_FLAG == last)
        goto error;
    buf = (U08 *)FS_MALLOC(SEC_SIZE);
    if(NULL == buf)
        goto error;
    if(E_ERR == FS_READ(last, buf))                        
        goto error;

    // 4. 创建一个 FILE_ENTRY 数据结构并初始化，并将其写到最后一个扇区的数据末尾处
    fe = (FILE_ENTRY *)(buf + root->lastBytes);             // fe 指向最后一个扇区的数据末尾处
    StrCpy(fe->name, name, -1);
    fe->fileBegin = SEC_END_FLAG;
    fe->fileNum = 0;
    fe->lastBytes = 0;
    fe->type = E_FILE;
    fe->inSecIdx = last;
    fe->inSecOff = root->lastBytes;
    if(E_ERR == FS_WRITE(last, buf))                        // 将 buf 中数据写回硬盘中              
        goto error;
    root->lastBytes += sizeof(FILE_ENTRY);
    if(E_ERR == FS_WRITE(1, (U08 *)root))                   // 将 root 中数据写回硬盘中              
        goto error;
    
    ret = E_OK;

error:
    FS_FREE(root);
    FS_FREE(buf);
    FS_FREE(fe);
    return ret;
}

/******************************************************************************
* 函数名称: static E_RET FreeFile(U32 secBegin)
* 功能说明: 删除 secBegin 关联的所有扇区
* 输入参数: U32 secBegin      --扇区号
* 输出参数: 无
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 无
******************************************************************************/
static E_RET FreeFile(U32 secBegin)
{
    U32 next = secBegin;

    while (SEC_END_FLAG != next)
    {
        if(E_ERR == FreeSector(next))
            goto error;
        next = NextSector(next);
    }
    return E_OK;

error:
    return E_ERR;
}

/******************************************************************************
* 函数名称: static BOOL IsOpened(const U08* name)
* 功能说明: 判断文件是否已经打开
* 输入参数: const U08* name      --文件名
* 输出参数: 无
* 函数返回: RRUE:已打开; FALSE:未打开
* 其它说明: 无
******************************************************************************/
static BOOL IsOpened(const U08* name)
{
    BOOL ret = FALSE;
    LIST_NODE* pListNode = NULL;
    FILE_DESC* fd = NULL;

    LIST_FOR_EACH(&fdList, pListNode)
    {
        fd = (FILE_DESC *)LIST_NODE(pListNode,  FILE_DESC, node);
        if(TRUE == StrCmp(fd->fe.name, name, -1))
        {
            ret = TRUE;
            break;
        }
    }

    return ret;
}

/******************************************************************************
* 函数名称: E_RET DeleteFileInRoot(const U08* name)
* 功能说明: 在根目录 root 下删除一个文件
* 输入参数: const U08* name      --要删除的文件名
* 输出参数: 无
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 无
******************************************************************************/
E_RET DeleteFileInRoot(const U08* name)
{
    E_RET ret = E_ERR;
    FILE_ENTRY* fe = NULL;
    U32 last = SEC_END_FLAG;
    FS_ROOT* root = NULL;
    U08* buf = NULL;
    U08* buf2 = NULL;
    FILE_ENTRY* last_fe = NULL;
    U32 prev = SEC_END_FLAG;
    U32 offset = 0;
    U32 idxOff = 0;
    U32 secOff = 0;
    U32* pUnit = NULL;
    FS_HEADER* header = NULL;
    FILE_ENTRY* tmp = NULL;

    // 1. 查找文件是否已经存在，只有文件存在方可删除
    fe = FindInRoot(name);                                  
    if(NULL == fe || TRUE == IsOpened(name))                // 文件不存在或者文件已打开
        goto error; 

    // 2. 释放删除文件相关的全部扇区
    if(E_ERR == FreeFile(fe->fileBegin))
        goto error;

    // 3. 找到最后一个 FILE_ENTRY，替换掉要删除的 FILE_ENTRY
    root = (FS_ROOT *)FS_MALLOC(SEC_SIZE);                  // 申请一个扇区大小的内存用于根目录信息（扇区 1）数据处理使用
    if(NULL == root)
        goto error;
    if(E_ERR == FS_READ(1, (U08 *)root))                    // 将扇区 1 中数据读到 root 内存中
        goto error;
    last = FindLast(root->rootBegin);
    buf = (U08 *)FS_MALLOC(SEC_SIZE);
    if(NULL == buf)
        goto error;
    if(E_ERR == FS_READ(last, buf))                         // 将扇区 last 中数据读到 buf 缓存中
        goto error;

    // 找到最后一个 FILE_ENTRY 的基址，将最后一个 FILE_ENTRY 拷贝到删除的 FILE_ENTRY 处，注意其中 inSecIdx 和 inSecOff 不需要拷贝
    last_fe = (FILE_ENTRY *)(buf + root->lastBytes - sizeof(FILE_ENTRY));
    if(E_ERR == FS_READ(last, buf))                         // 将扇区 last 中数据读到 buf 缓存中
        goto error;
    buf2 = (U08 *)FS_MALLOC(SEC_SIZE);                      // 申请 SEC_SIZE 大小的内存
    if(NULL == buf2)
        goto error;
    if(E_ERR == FS_READ(fe->inSecIdx, buf2))                // 将 fe 所在的整个扇区数据读到 buf2 缓存中
        goto error;
    tmp = (FILE_ENTRY *)(buf2+fe->inSecOff);                // tmp 指向要删除的 FILE_ENTRY 
    StrCpy(tmp->name, last_fe->name, -1);
    tmp->fileBegin = last_fe->fileBegin;
    tmp->fileNum = last_fe->fileNum;
    tmp->lastBytes = last_fe->lastBytes;
    tmp->type = last_fe->type;
    if(E_ERR == FS_WRITE(last_fe->inSecIdx, buf))           // 将 buf 中数据写回硬盘中  
        goto error;
    if(E_ERR == FS_WRITE(tmp->inSecIdx, buf2))              // 将 buf2 中数据写回硬盘中  
        goto error;

    // 4. 检查 lastBytes 是否为 0，如果为 0，则需要删除最后一个扇区
    root->lastBytes -= sizeof(FILE_ENTRY);
    if(E_ERR == FS_WRITE(1, (U08 *)root))                   // 将 root 缓冲中数据写回硬盘中
        goto error;
    if(0 == root->lastBytes)
    {
        root->rootNum--;
        FreeSector(last);                                   // 释放最后一个扇区
        prev = FindPrev(root->rootBegin, last);             // 找到 last 节点的上一个节点
        if(SEC_END_FLAG == prev)
            goto error;

        // 计算物理扇区号 prev 在扇区分配表中的位置
        header = (FS_HEADER *)FS_MALLOC(SEC_SIZE);          // 申请一个扇区大小的内存用于文件概要信息（扇区 0）数据处理使用
        if(NULL == header)
            goto error;
        offset = prev - header->mapNum - 2;                 // 计算 prev 这个绝对地址（物理扇区号）在扇区分配表中对应的管理单元的相对偏移位置
        if(offset >= header->mapNum)                        // prev 范围超限
            goto error;
        secOff = offset / MAP_UNIT_NUM;                     // 计算目标扇区对应的管理单元处在扇区分配表中的第几个扇区，secOff+2 才是其绝对地址（物理扇区号）
        idxOff = offset % MAP_UNIT_NUM;                     // 计算目标扇区对应的管理单元处在扇区分配表中某一扇区中的偏移量
        pUnit = (U32*)FS_MALLOC(SEC_SIZE);                  // 申请一个扇区大小的内存用于扇区分配表数据处理
        if(NULL == pUnit)
            goto error;
        if(E_ERR == FS_READ(secOff, (U08 *)pUnit))          // 将扇区号 prev 对应的管理单元所在的整个扇区读到 pUnit 缓冲中
            goto error;
        *(pUnit + idxOff) = SEC_END_FLAG;                   // 置结束标志
        if(E_ERR == FS_WRITE(secOff, (U08 *)pUnit))         // 将 pUnit 缓冲中数据写回硬盘中
            goto error;
    }
    if(E_ERR == FS_WRITE(1, (U08 *)root))                   // 将 root 缓冲中数据写回硬盘中
        goto error;

error:
    FS_FREE(buf);
    FS_FREE(root);
    FS_FREE(pUnit);
    FS_FREE(fe);
    return ret;
}

/******************************************************************************
* 函数名称: E_RET RenameFileInRoot(const U08* old, const U08* new)
* 功能说明: 文件重命名
* 输入参数: const U08* old          --旧文件名
    　　　　const U08* new          --新文件名
* 输出参数: 无
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 无
******************************************************************************/
E_RET RenameFileInRoot(const U08* old, const U08* new)
{
    E_RET ret = E_ERR;
    FILE_ENTRY* fe = NULL;
    U08* buf = NULL;

    // 检查参数合法性
    if(NULL == old || NULL == new)
        goto error;

    fe = FindInRoot(new);
    if(NULL != fe || TRUE == IsOpened(old))                 // 根目录下已有名为 new 的文件了或者文件已打开
        goto error;
    fe = FindInRoot(old);
    if(NULL == fe)                                          // 要被替换的文件名不存在
        goto error;

    // 找到 old 文件对应的 FILE_ENTRY 所在位置，从硬盘中读取整个扇区，修改其中 name，然后再写回硬盘
    buf = (U08 *)FS_MALLOC(SEC_SIZE);
    if(NULL == buf)
        goto error;
    if(E_ERR == FS_READ(fe->inSecIdx, buf))
        goto error; 
    StrCpy(((FILE_ENTRY *)(buf + fe->inSecOff))->name, new, -1);
    if(E_ERR == FS_WRITE(fe->inSecIdx, buf))
        goto error;

error:
    FS_FREE(fe);
    FS_FREE(buf);
    return ret;
}

/******************************************************************************
* 函数名称: static U32 FindIndex(U32 secBegin, U32 idx)
* 功能说明: 得到 secBegin 链表中的第 idx 节点对应的物理扇区号
* 输入参数: U32 secBegin		--链表起始扇区号
    　　　　U32 idx             --节点偏移值
* 输出参数: 无
* 函数返回: 物理扇区号
* 其它说明: 无
******************************************************************************/
static U32 FindIndex(U32 secBegin, U32 idx)
{
    U32 ret = secBegin;
    U32 i = 0;

    while(i < idx && ret != SEC_END_FLAG)
    {
        ret = NextSector(ret);
        i++;
    }

    return ret;
}

/******************************************************************************
* 函数名称: FILE_DESC* FileOpen(const U08* name)
* 功能说明: 打开文件
* 输入参数: const U08* name          --文件名
* 输出参数: 无
* 函数返回: FILE_DESC*               --文件描述符
* 其它说明: 无
******************************************************************************/
FILE_DESC* FileOpen(const U08* name)
{
    FILE_DESC* fd = NULL;
    FILE_ENTRY* fe = NULL;

    // 检查参数合法性
    if(NULL == name)
        goto error;

    // 文件不可以重复打开
    if(TRUE == IsOpened(name))
        goto error;

    // 找到 fe
    fe = FindInRoot(name);
    if(NULL == fe)
        goto error;

    // 申请一块内存用于存放文件描述符数据结构
    fd = (FILE_DESC *)FS_MALLOC(sizeof(FILE_DESC));
    if(NULL == fd)
        goto error;

    // 初始化文件描述符 fd，然后将其添加到链表 fdList 中
    fd->fe = *fe;
    fd->secIdx = 0;
    fd->offset = 0;
    fd->changed =0;
    ListAddHead(&fdList, &(fd->node));

error:
    FS_FREE(fe);
    return fd;
}


/******************************************************************************
* 函数名称: static E_RET Flush(FILE_DESC* fd)
* 功能说明: 文件更新后，相关数据写回硬盘
* 输入参数: FILE_DESC* fd       --文件描述符
* 输出参数: 无
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 无
******************************************************************************/
static E_RET Flush(FILE_DESC* fd)
{
    U32 secNum = SEC_END_FLAG;
    U08* buf = NULL;
    E_RET ret = E_ERR;

    // 当参数 fd 为空或者缓冲区没有变动时直接退出 
    if(NULL == fd || 0 == fd->changed)
        return E_ERR;

    // 得到当前变动缓冲区对应的物理扇区号，然后将改动后的 catch[SEC_SIZE] 数据该扇区中
    secNum = FindIndex(fd->fe.fileBegin, fd->secIdx);
    if(SEC_END_FLAG == secNum)
        return E_ERR;
    if(E_ERR == FS_WRITE(secNum, fd->catch))
        return E_ERR;
    fd->changed = 0;                                        // 缓冲区数据已成功写入硬盘，清变动标志

    // 当文件数据更新后，该文件对应的 FILE_ENTRY 也有可能变化
    // 比如文件追加写入数据后 lastBytes 会增加，此时需要重新将 FILE_ENTRY 写回硬盘
    // 这里为了简单就不判断 FILE_ENTRY 是否改动了，直接重新写回硬盘
    buf = (U08 *)FS_MALLOC(SEC_SIZE);
    if(NULL == buf)
        return E_ERR;
    if(E_ERR == FS_READ(fd->fe.inSecIdx, buf))
        goto error; 
    *((FILE_ENTRY *)(buf + fd->fe.inSecOff)) = fd->fe;
    if(E_ERR == FS_WRITE(fd->fe.inSecIdx, buf))
        goto error;

    ret = E_OK;

error:
    FS_FREE(buf);
    return ret;
}

/******************************************************************************
* 函数名称: E_RET FileClose(FILE_DESC* fd)
* 功能说明: 关闭文件
* 输入参数: FILE_DESC* fd       --文件描述符
* 输出参数: 无
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 无
******************************************************************************/
E_RET FileClose(FILE_DESC* fd)
{
    LIST_NODE* pListNode = NULL;

    // 遍历 fdList，如果找到了文件描述符 fd，则将缓冲区中文件数据写到硬盘中并释放 fd
    LIST_FOR_EACH(&fdList, pListNode)
    {
        if(fd == (FILE_DESC *)LIST_NODE(pListNode,  FILE_DESC, node))
        {
            Flush(fd);                                      // 将缓冲区中文件数据写到硬盘中
            ListDelNode(&(fd->node));                       // 删除链表节点
            FS_FREE(fd);
            return E_OK;
        }
    }
    
    return E_ERR;
}

/******************************************************************************
* 函数名称: E_RET FsFormat(void)
* 功能说明: 硬盘格式化
* 输入参数: 无
* 输出参数: 无
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 无
******************************************************************************/
static E_RET FsFormat(void)
{
    E_RET ret = E_ERR;
    FS_HEADER* header = (FS_HEADER *)FS_MALLOC(SEC_SIZE);  // 申请一个扇区大小的内存用于文件概要信息（扇区 0）数据处理使用
    FS_ROOT* root = (FS_ROOT *)FS_MALLOC(SEC_SIZE);        // 申请一个扇区大小的内存用于根目录信息（扇区 1）数据处理使用
    U32* pUnit = (U32*)FS_MALLOC(SEC_SIZE);                // 申请一个扇区大小的内存用于扇区分配表数据处理
    U32 i = 0;
    U32 j = 0;
    U32 current = 0;

    if(NULL == header || NULL == root || NULL == pUnit)
        goto error;

    // 初始化扇区 0，即将文件系统概要信息写到扇区 0 中
    StrCpy(header->magic, FS_MAGIC, -1);
    header->sctNum = FS_SEC_NUM;
    // 计算扇区分配表所占扇区数
    // header->sctNum - 2 是因为扇区 0 和 扇区 1 被特定使用，这两个扇区需要先被踢除
    // + !!((header->sctNum - 2) % (SEC_SIZE / 4)) 是因为扇区分配表中最后一个扇区并不一定是满的。不足一个扇区也按一个扇区算
    header->mapNum = (header->sctNum - 2) / (SEC_SIZE / 4) + !!((header->sctNum - 2) % (SEC_SIZE / 4));
    // 格式化时第一个空闲的扇区号即为扇区分配表后的第一个扇区的扇区号
    header->freeBegin = 2 + header->mapNum;
    // 格式化时空闲扇区数等于总扇区数减去扇区 0 和 扇区 1 这两个固定的扇区，然后再减去扇区分配表所占的扇区数
    header->freeNum = header->sctNum - 2 - header->mapNum; 
    // 写到硬盘扇区 0 中
    if(E_ERR == FS_WRITE(0, (U08 *)header))
        goto error;

    // 初始化扇区 1，即将根目录相关信息写到扇区 1 中
    StrCpy(root->magic, ROOT_MAGIC, -1);
    // 格式化时根目录下为空，也可以看成是根目录这个文件没有数据，不占用一个扇区
    root->rootBegin = SEC_END_FLAG;
    root->rootNum = 0;
    root->lastBytes = 0;
    // 写到硬盘扇区 1 中
    if(E_ERR == FS_WRITE(1, (U08 *)root))
        goto error;

    // 初始化扇区分配表
    for(i = 0; (i < header->mapNum) && (current < header->freeNum); i++)
    {
        for(j = 0; j < MAP_UNIT_NUM; j++)
        {
            *(pUnit+j) = current + 1;
            current++;
            // 如果是最后一个扇区管理单元，则将其内数值设置为结束标志，并且跳出循环
            if(current == header->freeNum)
            {
                *(pUnit+j) = SEC_END_FLAG;
                break;
            }
        }
        // 写到硬盘扇区分配表中
        if(E_ERR == FS_WRITE(2+i, (U08 *)pUnit))
            goto error;
    }
    ret = E_OK;

error:
    FS_FREE(header);
    FS_FREE(root);
    FS_FREE(pUnit);
    return ret;
}

/******************************************************************************
* 函数名称: static E_RET FsIsFormatted(void)
* 功能说明: 判断硬盘是已经被格式化
* 输入参数: 无
* 输出参数: 无
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 如果硬盘未被格式化，则格式化硬盘，如果已经格式化过，则不再格式化
******************************************************************************/
static E_RET FsIsFormatted(void)
{
    E_RET ret = E_OK;
    FS_HEADER* header = (FS_HEADER *)FS_MALLOC(SEC_SIZE);  // 申请一个扇区大小的内存用于文件概要信息（扇区 0）数据处理使用

    if(NULL == header)
        return E_ERR;

    // 读取扇区 0 中的数据，比较 header->magic 中字符串是否等于 FS_MAGIC 
    // 如果相等，则证明硬盘已经被格式化过；如果不相等，则格式化硬盘
    if(E_ERR == FS_READ(0, (U08 *)header))
    {
        FS_FREE(header);
        return E_ERR;
    }
    if(FALSE == StrCmp(FS_MAGIC, header->magic, -1))
        ret = FsFormat();

    FS_FREE(header);
    return ret;
}

/******************************************************************************
* 函数名称: E_RET FsInit(void)
* 功能说明: 文件系统初始化
* 输入参数: 无
* 输出参数: 无
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 无
******************************************************************************/
E_RET FsInit(void)
{
    ListInit(&fdList);
    return FsIsFormatted();
}

// 测试
void FsTest(void)
{
    printk("creat:%d\n", CreatFileInRoot("1.txt"));
    
    FILE_DESC* fd = FileOpen("1.txt");
    printk("%d\n", IsOpened("1.txt"));

    FileClose(fd);
    printk("%d\n", IsOpened("1.txt"));
}

